package org.flyan.ffsm.core.event;

import org.flyan.ffsm.core.action.Action;
import org.flyan.ffsm.core.arg.Args;
import org.flyan.ffsm.core.exception.EventNotBindStateException;
import org.flyan.ffsm.core.sm.StateMachine;
import org.flyan.ffsm.core.state.State;

import java.io.Serializable;

/**
 * 事件
 * <p>
 * 表示一个现实事件或者是其他状态改变事件等。。。
 * 一个事件的发生，常常会触发其他事件发生或者状态的改变，我们将其称之为动作。
 * <p>
 * 一个事件需要给定一个能唯一表示该事件的标志，该标志建议使用枚举类，当然，
 * 如若喜欢，自己编写统一状态标志的类来表示也是极为不错的，若这样，该类必须
 * 实现 hashCode() 方法。
 *
 * @param <ST> 状态标志
 * @param <ET> 事件标志
 * @author flyan
 * @version 1.0.0
 * @date 6/23/22
 * @since 1.0.0
 */
public class Event<ST, ET>
        implements Serializable, Cloneable {

    private static final long serialVersionUID = 7182072334642149664L;

    /**
     * 事件标志，唯一表示一个发生的事件
     */
    private final ET eventTag;

    /**
     * 事件触发的动作
     */
    private final Action<ST, ET> action;

    /**
     * 事件发生时的状态
     */
    private State<ST, ET> currState;

    public Event(ET eventTag, Action<ST, ET> action) {
        this.eventTag = eventTag;
        this.action = action;
    }

    public Event(ET eventTag, State<ST, ET> currState, Action<ST, ET> action) {
        this(eventTag, action);
        this.currState = currState;
    }

    /**
     * 当前事件发生，触发动作
     *
     * @return 事件后的下一状态
     */
    public State<ST, ET> act(Args args) {
        if(this.currState == null) {
            throw new EventNotBindStateException();
        }
        return action.act(this, args);
    }

    /**
     * 绑定状态
     *
     * @param state
     */
    public void bindState(State<ST, ET> state) {
        if(this.currState == null) {
            this.currState = state;
        }
    }

    public StateMachine<ST, ET> getStateMachine() {
        return currState != null ? currState.getStateMachine() : null;
    }

    /**
     * 获取事件标志
     *
     * @return
     */
    public ET getEventTag() {
        return eventTag;
    }

    public Event<ST, ET> setCurrState(State<ST, ET> currState) {
        this.currState = currState;
        return this;
    }

    /**
     * 获取当前状态
     *
     * @return
     */
    public State<ST, ET> getCurrState() {
        return currState;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Event<?, ?> event = (Event<?, ?>) o;
        return eventTag.equals(event.eventTag);
    }

    @Override
    public int hashCode() {
        return eventTag.hashCode();
    }

    @Override
    public String toString() {
        return "Event{" +
                "eventTag=" + eventTag +
                ", currState.stateTag=" + currState.getStateTag() +
                '}';
    }

    @Override
    public Event<ST, ET> clone() throws CloneNotSupportedException {
        @SuppressWarnings("unchecked") Event<ST, ET> clone = (Event<ST, ET>) super.clone();
        return clone;
    }

}